package com.zhou.datapick;


import com.zhou.util.ListUtil;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.MapUtils;

import java.util.*;

/**
 * @author lang.zhou
 * @since 2022/11/16
 */
public class InnerJoiner<T> {

    private static final String SEC = "<|-|>";

    @Getter
    private Map<DataKey, DataUnit<T>> joinUnitMap = new LinkedHashMap<>();

    private Map<String,Set<JoinRelation>> relationMap = new HashMap<>();

    private Map<DataKey,Set<DataKey>> updateMap = new HashMap<>();

    @Setter
    private ValueGetter<T> getter;

    public InnerJoiner(Collection<JoinRelation> joinRelations) {
        addRelations(joinRelations);
    }



    private String getJoin(T t, Collection<JoinRelation> col,boolean left){
        /*JoinRelation first = ListUtil.getFirst(col);
        String s = first.getLeftKey() + "||" + first.getRightKey();
        String join = t.getJoin().get(s);
        if(join == null){
            StringJoiner j = new StringJoiner("||");
            col.forEach(b->{
                String name = left ? b.getLeftColumn():b.getRightColumn();
                String v = getter.getValue(t.getData(),name);
                j.add(v);
            });
            t.getJoin().put(s,join = j.toString());
        }
        return join;*/
        StringBuilder j = new StringBuilder();
        col.forEach(b->{
            String name = left ? b.getLeftColumn():b.getRightColumn();
            String v = getter.getValue(t,name);
            j.append(v).append("||");
        });
        return j.toString();
    }


    public InnerJoiner<T> addUnit(String name , List<T> unit){
        DataKey dataKey = new DataKey(name);
        joinUnitMap.put(dataKey,new DataUnit<>(dataKey, unit));
        return this;
    }

    public InnerJoiner <T> addUnit(DataUnit<T> unit){
        joinUnitMap.put(unit.getKey(),unit);
        return this;
    }

    public InnerJoiner<T> addUnits(Collection<DataUnit<T>> collection){
        for (DataUnit<T> unit : collection) {
            addUnit(unit);
        }
        return this;
    }

    public List<JoinRelation> getRelations(){
        List<JoinRelation> c = new ArrayList<>();
        for (Map.Entry<String, Set<JoinRelation>> entry : relationMap.entrySet()) {
            c.addAll(entry.getValue());
        }
        return c;
    }

    private void addRelations(Collection<JoinRelation> relationList){
        if(relationList != null && relationList.size() > 0){
            for (JoinRelation relation : relationList) {
                String a = relation.getLeftKey()+ SEC + relation.getRightKey();

                Set<JoinRelation> relations = relationMap.computeIfAbsent(a, l -> new HashSet<>(1));
                if(!relations.contains(relation)){
                    relations.add(relation);
                    updateMap.computeIfAbsent(relation.getLeftKey(),l->new HashSet<>(1)).add(relation.getRightKey());
                }
            }
        }
    }


    public void join(){
        for (DataKey dataKey : joinUnitMap.keySet()) {
            DataUnit<T> left = joinUnitMap.get(dataKey);
            Set<DataKey> rightNames = updateMap.get(dataKey);
            if(left != null && rightNames!= null){
                for (DataKey rightName : rightNames) {
                    joinDataKey(dataKey,rightName);
                }
            }
        }

    }

    public void joinDataKey(DataKey left, DataKey right){
        DataUnit<T> l = joinUnitMap.get(left);
        DataUnit<T> r = joinUnitMap.get(right);
        if(l!= null && r != null){
            Set<JoinRelation> joinRelations = relationMap.get(left + SEC + right);
            joinList(l,r,joinRelations);
            Set<DataKey> ru = updateMap.get(right);
            if(ru != null){
                for (DataKey s : ru) {
                    //和下级进行关联
                    joinDataKey(right,s);
                    //关联结束再次和上级关联
                    joinList(l,r,joinRelations);
                }
            }
        }
    }


    /*public void joinList(DataUnit<T> list1, DataUnit<T> list2, Collection<JoinRelation> relations){
        if(list1.size() == 0 || list2.size() == 0){
            list1.clear();
            list2.clear();
            return;
        }
        Set<String> kv1 = new HashSet<>(list1.size());
        list1.forEach(o->kv1.add(getJoin(o,relations,true)));

        Set<String> kv2 = new HashSet<>(list2.size());
        for (Iterator<DataCell<T>> iterator = list2.iterator(); iterator.hasNext(); ) {
            DataCell<T> cell = iterator.next();
            String join = getJoin(cell,relations,false);
            if(!kv1.contains(join)){
                iterator.remove();
            }else{
                kv2.add(join);
            }
        }
        list1.removeIf(o-> !kv2.contains(o.getJoin()));
    }*/

    public void joinList(DataUnit<T> list1, DataUnit<T> list2, Collection<JoinRelation> relations){

        if(list1.size() == 0 || list2.size() == 0){
            list1.clear();
            list2.clear();
            return;
        }
        Set<String> kv1 = new HashSet<>(list1.size());
        Set<String> kv2 = new HashSet<>(list2.size());
        list1.forEach(o->kv1.add(getJoin(o,relations,true)));
        list2.forEach(o->kv2.add(getJoin(o,relations,false)));

        list1.removeIf(o->!kv2.contains(getJoin(o,relations,true)));
        list2.removeIf(o->!kv1.contains(getJoin(o,relations,false)));
    }

    public DataUnit<T> getResult(DataKey key){
        return this.joinUnitMap.get(key);
    }

    public T getResult(DataKey key, Integer index){
        DataUnit<T> maps = this.joinUnitMap.get(key);
        if(maps != null && maps.size() > index){
            return maps.get(index);
        }
        return null;
    }

    public Map<DataKey, T> findRow(T map, DataKey key){
        DataUnit<T> d = new DataUnit<>(key, Collections.singletonList(map));
        List<JoinRelation> relations = this.getRelations();

        InnerJoiner<T> j = new InnerJoiner<>(relations);
        j.addUnit(d);
        j.setGetter(getter);
        Map<DataKey, DataUnit<T>> unitMap = j.getJoinUnitMap();

        for (Map.Entry<DataKey, DataUnit<T>> entry : joinUnitMap.entrySet()) {
            if(!unitMap.containsKey(entry.getKey())){
                j.addUnit(new DataUnit<>(entry.getValue()));
            }
        }

        j.join();

        Map<DataKey, T> res = new HashMap<>();

        for (Map.Entry<DataKey, DataUnit<T>> entry : unitMap.entrySet()) {
            res.put(entry.getKey(), ListUtil.getFirst(entry.getValue()));
        }
        return res;
    }



    public static void main(String[] args) {

        test();
    }

    private static void test(){
        long t0 = System.currentTimeMillis();
        List<Map<String,Object>> list1 = new ArrayList<>();
        List<Map<String,Object>> list2 = new ArrayList<>();
        List<Map<String,Object>> list3 = new ArrayList<>();
        //构造10万条数据
        for (int i = 0; i < 100000; i++) {
            String uuid = UUID.randomUUID().toString();

            Map<String,Object> m1 = new HashMap<>();
            m1.put("id1",i);
            m1.put("value1", uuid.charAt(10)+uuid.charAt(12));

            Map<String,Object> m2 = new HashMap<>();
            m2.put("id2",i);
            m2.put("value2", uuid.charAt(10)+uuid.charAt(1));

            Map<String,Object> m3 = new HashMap<>();
            m3.put("id3",i);
            m3.put("value3", uuid.charAt(10)+uuid.charAt(12));

            list1.add(m1);
            list2.add(m2);
            list3.add(m3);
        }
        long t1 = System.currentTimeMillis();
        System.out.println("构造数据耗时："+(t1-t0));
        //添加关联条件
        ArrayList<JoinRelation> joinRelations = new ArrayList<>();
        joinRelations.add(new JoinRelation("list1.id1=list2.id2"));
        joinRelations.add(new JoinRelation("list1.value1=list2.value2"));
        joinRelations.add(new JoinRelation("list2.id2=list3.id3"));
        joinRelations.add(new JoinRelation("list2.value2=list3.value3"));

        InnerJoiner<Map<String,Object>> joiner = new InnerJoiner<>(joinRelations);
        //字段逻辑
        joiner.setGetter((o, columnName) -> MapUtils.getString(o,columnName,""));

        long t11 = System.currentTimeMillis();
        System.out.println("构造join对象耗时："+(t11-t1));

        //添加数据集
        joiner.addUnit("list1",list1)
                .addUnit("list2",list2)
                .addUnit("list3",list3);
        //进行关联操作
        joiner.join();

        DataKey key = new DataKey("list1");
        //通过key拿到关联结果集，每个结果集数据条数一样
        DataUnit<Map<String, Object>> result = joiner.getResult(key);
        System.out.println("关联条数："+result.size());

        long t2 = System.currentTimeMillis();
        System.out.println("关联耗时："+(t2-t11));
        //遍历其中一个数据集
        for (Map<String, Object> map : result) {
            //找到与该行关联的数据集，如果关联到多条，只会返回第一条
            Map<DataKey, Map<String, Object>> row = joiner.findRow(map, key);
            int i = 0;
        }
        long t3 = System.currentTimeMillis();
        System.out.println("单行遍历查找耗时："+(t3-t2));
    }

}